//!绑定（Binding）
// 重要：首先必须强调下，准确地说Rust中并没有变量这一概念，而应该称为标识符，目标资源(内存，存放value)绑定到这个标识符：

// Rust并不会像其他语言一样可以为变量默认初始化值，Rust明确规定变量的初始值必须由程序员自己决定

//let关键字并不只是声明变量的意思，它还有一层特殊且重要的概念-绑定。通俗的讲，let关键字可以把一个标识符和一
// 段内存区域做“绑定”，绑定后，这段内存就被这个标识符所拥有，这个标识符也成为这段内存的唯一所有者。
// 所以，a = 100发生了这么几个动作，首先在栈内存上分配一个i32的资源，并填充值100，随后，把这个资源与a做绑定，
// 让a成为资源的所有者(Owner)。

//作用域,像C语言一样，Rust通过{}大括号定义作用域：
//局部变量离开作用域后，变量随即会被销毁；但不同是，Rust会连同变量绑定的内存，不管是否为常量字符串，连同所有者变量一起被销毁释放

#[test]
pub fn demo1(){
    {
        let x: i32;       // 标识符x, 没有绑定任何资源
        let y: i32 = 100; // 标识符y，绑定资源100
        //println!("{}", x); //会收到一个error报错,初始值必须由程序员自己决定
    }
}

//移动语义（move）
//Rust规定，只有资源的所有者销毁后才释放内存，而无论这个资源是否被多次move，同一时刻只有一个owner，
// 所以该资源的内存也只会被free一次。 通过这个机制，就保证了内存安全
#[test]
pub fn demo2(){
    {
        let a: String = String::from("xyz");
        //把资源的所有权(ownership)从一个绑定转移（move）成另一个绑定
        let b = a;
        //println!("{}", a); //编译后会得到如下的报错：value borrowed here after move
    }
}

//对于实现Copy特性的变量，在move时会拷贝资源到新内存区域
//在Rust中，基本数据类型(Primitive Types)均实现了Copy特性，包括i8, i16, i32, i64, usize, u8, u16,
// u32, u64, f32, f64, (), bool, char等等
#[test]
pub fn demo3(){
    let a: i32 = 100;
    //对于实现Copy特性的变量，在move时会拷贝资源到新内存区域，并把新内存区域的资源binding为b。
    let b = a;
    println!("{}", a); //编译确实可以通过，输出为100
}

//浅拷贝与深拷贝
//对于基本数据类型来说，“深拷贝”和“浅拷贝“产生的效果相同。对于引用对象类型来说，”浅拷贝“更像仅仅拷贝了对象的内存地址。
// 如果我们想实现对String的”深拷贝“怎么办？ 可以直接调用String的Clone特性实现对内存的值拷贝而不是简单的地址拷贝。

#[test]
pub fn demo4(){
    let a: String = String::from("xyz");
    let b = a.clone();  // <-注意此处的clone
    println!("{}", a);
}

//可变性,不能对不可变绑定赋值。如果要修改值，必须用关键字mut声明绑定为可变的,
//不可变绑定与const常量是完全不同的两种概念；首先，“不可变”准确地应该称为“不可变绑定”，是用来约束绑定行为的，
// “不可变绑定”后不能通过原“所有者”更改资源内容。

//“可变绑定”后，目标内存还是同一块，只不过，可以通过新绑定的a去修改这片内存了。
#[test]
pub fn demo5(){
    let mut a: i32 = 100;

    a = 33;

}

//高级Copy特性
//一旦一种类型实现了Copy特性，这就意味着这种类型可以通过的简单的位(bits)拷贝实现拷贝。从前面知识我们知道“绑定”
// 存在move语义（所有权转移），但是，一旦这种类型实现了Copy特性，会先拷贝内容到新内存区域，然后把新内存区域和这个标识符做绑定。

//哪些情况下我们自定义的类型（如某个Struct等）可以实现Copy特性？
// 只要这种类型的属性类型都实现了Copy特性，那么这个类型就可以实现Copy特性。 例如：

#[test]
pub fn demo6(){
    struct Foo {  //可实现Copy特性
        a: i32,
        b: bool,
    }

    struct Bar {  //不可实现Copy特性
        l: Vec<i32>,
    }
    //因为Foo的属性a和b的类型i32和bool均实现了Copy特性，所以Foo也是可以实现Copy特性的。但对于Bar来说，
    // 它的属性l是Vec<T>类型，这种类型并没有实现Copy特性，所以Bar也是无法实现Copy特性的。
}

//那么我们如何来实现Copy特性呢？ 有两种方式可以实现。
//1.通过derive让Rust编译器自动实现
#[test]
pub fn demo7(){
    //编译器会自动检查Foo的所有属性是否实现了Copy特性，一旦检查通过，便会为Foo自动实现Copy特性。
    #[derive(Copy, Clone)]
    struct Foo {
        a: i32,
        b: bool,
    }
}
//2.手动实现Clone和Copy trait

#[derive(Debug)]
struct Foo {
    a: i32,
    b: bool,
}
impl Copy for Foo {}
impl Clone for Foo {
    fn clone(&self) -> Foo {
        Foo{a: self.a, b: self.b}
    }
}
#[test]
pub fn demo8() {
    let x = Foo{ a: 100, b: true};
    let mut y = x;
    y.b = false;

    println!("{:?}", x);  //打印：Foo { a: 100, b: true }
    println!("{:?}", y);  //打印：Foo { a: 100, b: false }
}

//高级move
//let绑定会发生所有权转移的情况，但ownership转移却因为资源类型是否实现Copy特性而行为不同：
//let x: T = something;
// let y = x;

//类型T没有实现Copy特性：x所有权转移到y。
// 类型T实现了Copy特性：拷贝x所绑定的资源为新资源，并把新资源的所有权绑定给y，x依然拥有原资源的所有权。


//move关键字,move关键字常用在闭包中，强制闭包获取所有权。

#[test]
pub fn demo9(){
    let x: i32 = 100;
    //使不使用 move 对结果都没什么影响，因为x绑定的资源是i32类型
    let some_closure = move |i: i32| i + x;
    let y = some_closure(2);
    println!("x={}, y={}", x, y); // x=100, y=102

}
#[test]
pub fn demo10(){
    let mut x: String = String::from("abc");
    let mut some_closure = move |c: char| x.push(c);
    let y = some_closure('d');
    //println!("x={:?}", x); //value borrowed here after move
    //这是因为move关键字，会把闭包中的外部变量的所有权move到包体内，发生了所有权转移的问题，
    // 所以println访问x会如上错误。如果我们去掉println就可以编译通过。
}

//那么，如果我们想在包体外依然访问x，即x不失去所有权，怎么办？
//我们只是去掉了move，去掉move后，包体内就会对x进行了可变借用，而不是“剥夺”x的所有权，细心的同学还注意到我们在
// 前后还加了{}大括号作用域，是为了作用域结束后让可变借用失效，这样println才可以成功访问并打印我们期待的内容。
#[test]
pub fn demo11(){
    let mut x: String = String::from("abc");
    {
        let mut some_closure = |c: char| x.push(c);
        some_closure('d');
    }
    println!("x={:?}", x);  //成功打印：x="abcd"
}